---------------------------------------------------------------------------------------------------- -- Physic vehicle control ---------------------------------------------------------------------------------------------------- -- Исходный скрипт: Evgeniy Negrobov (Jon) jon@gsc-game.kiev.ua -- Перевод на xr_logic: Andrey Fidrya (Zmey) af@svitonline.com -- Доработка для БТР: Oleg Kreptul (Haron) haronk@ukr.net ---------------------------------------------------------------------------------------------------- function printf() end local pi_2 = math.pi / 3 -- 60 degree local def_min_delta_per_sec = 0.2 local def_min_car_explode_time = 1000 local def_moving_speed = 10 --local def_arriving_factor = 0.4 local def_arriving_dist = 1.0 local def_not_rotating_angle = 0.4 local def_min_fire_time = 0 local def_update_time = 1.6 local def_fire_range = 50 local def_max_fc_upd_num = 1000 -- default maximum fastcall updates num local def_arriving_koef = 3 --70 local delay_after_ignition = 500 local state_none = 0 local state_moving_fwd = 1 local state_moving_rot_left = 2 local state_moving_rot_right = 3 local state_moving_stop = 4 local state_moving_end = 5 local state_cannon_rotate = 1 local state_cannon_follow = 2 local state_cannon_delay = 3 local state_cannon_stop = 4 local state_shooting_on = 1 local state_firetarget_points = 1 local state_firetarget_enemy = 2 -- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class "action_car" function action_car:__init(obj, storage) printf("car : init.") self.object = obj self.car = self.object:get_car() self.st = storage end function action_car:reset_scheme(loading) printf("car : START INITIALIZING ======================================================") printf("car : action_car:reset_scheme: self.object:name()='%s'", self.object:name()) self.destroyed = false self.object:set_nonscript_usable(false) self.object:set_tip_text("") self.show_tips = self.st.show_tips self.tip_use = self.st.tip_use self.tip_locked = self.st.tip_locked if self.st.invulnerable then self.def_health = self.object.health end self.headlights = object.deactivate if self.st.headlights == "on" then self.headlights = object.activate end printf("car : headlights %s", self.st.headlights) xr_logic.mob_capture(self.object, true) action(self.object, object("left_light", self.headlights), cond(cond.time_end, time_infinite)) xr_logic.mob_capture(self.object, true) action(self.object, object("right_light", self.headlights), cond(cond.time_end, time_infinite)) self.usable = self.st.usable --if self.usable ~= nil then -- self.car_holder = self.object.get_current_holder() --engaged() --else if self.usable == nil then self.car:SetExplodeTime(0) -- (-1) if self.car:HasWeapon() then printf("car : car has weapon.") self.car:Action(CCar.eWpnActivate,1) self.hasWeapon = true else printf("car : car hasn't weapon.") self.hasWeapon = false end if loading then self.speed = load_var(self.object, "speed") or def_moving_speed self.loop = load_var(self.object, "loop") or false self.min_delta_per_sec = load_var(self.object, "min_delta_per_sec") or def_min_delta_per_sec self.min_car_explode_time = load_var(self.object, "min_car_explode_time") or def_min_car_explode_time self.state_moving = load_var(self.object, "state_moving") or state_none else self.speed = def_moving_speed self.loop = false self.min_delta_per_sec = def_min_delta_per_sec self.min_car_explode_time = def_min_car_explode_time self.state_moving = state_none end --xr_logic.mob_capture(self.object, true) self.st.signals = {} self.fc_upd_num = 0 -- fastcall updates num self.fc_upd_avg = 10 -- average time of the fastcall updates (in millisecond) self.fc_last_upd_tm = -1 -- fastcall last update time self.last_pos = nil self.last_pos_time = 0 self.state_delaying = false --self.state_moving = state_none self.target_walk_pt = -1 -- path_walk if self.st.path_walk then self.path_walk = patrol(self.st.path_walk) if not self.path_walk then printf("object '%s': unable to find path_walk '%s' on the map", self.object:name(), self.st.path_walk) end self:start_car() self.path_walk_count = self.path_walk:count() --if not self.path_walk_info then self.path_walk_info = utils_obj.path_parse_waypoints(self.st.path_walk) if not self.path_walk_info then printf("object '%s': path_walk ('%s'): unable to obtain path_walk_info from path", self.object:name(), self.st.path_walk) end --end self.arrival_signalled = false self.target_dist = -1 if loading then self.target_walk_pt = load_var(self.object, "target_walk_pt") or self:get_nearest_walkpoint() if self.target_walk_pt == -1 then self.target_walk_pt = self:get_nearest_walkpoint() end else self.target_walk_pt = self:get_nearest_walkpoint() end printf("car : target_walk_pt.1(%d)", self.target_walk_pt) if self:at_target_walkpoint() then self:walk_arrival_callback(self.target_walk_pt) end self:go_to_walkpoint(self.target_walk_pt) else printf("car : target_walk_pt.2(-2)") self.target_walk_pt = -2 self.state_moving = state_moving_stop end printf("car : start target_pt1 = %d", self.target_walk_pt) self.state_firetarget = state_none self.state_cannon = state_none self.state_shooting = state_none self.target_fire_pt = nil self.target_fire_pt_idx = 0 self.target_obj = nil self.on_target_vis = nil self.on_target_nvis = nil if self.hasWeapon then --if loading then -- self.state_cannon = load_var(self.object, "state_cannon") -- self.state_shooting = load_var(self.object, "state_shooting") --end self:set_shooting(self.state_shooting) --printf("car : target = %s", self.st.fire_target) local n = 0 if self.st.fire_target == "points" then self.state_firetarget = state_firetarget_points else if self.st.fire_target == "actor" and db.actor:alive() then self.target_obj = db.actor self.state_firetarget = state_firetarget_enemy else n = tonumber(self.st.fire_target) if n then obj = level_object_by_sid(n) if obj and obj:alive() then self.target_obj = obj self.state_firetarget = state_firetarget_enemy end end end --if self.target_obj then -- self.target_ph_shell = self.target_obj:get_physics_shell() --end end self.fire_track_target = self.st.fire_track_target if self.st.on_target_vis then vis = self.st.on_target_vis if vis.v1 == "actor" then vis.v1 = db.actor self.on_target_vis = vis printf("car : target actor") else n = tonumber(vis.v1) if n then obj = level_object_by_sid(n) if obj and obj:alive() then vis.v1 = obj self.on_target_vis = vis printf("car : target %d", n) end end end end if self.st.on_target_nvis then nvis = self.st.on_target_nvis if nvis.v1 == "actor" then nvis.v1 = db.actor self.on_target_nvis = nvis printf("car : target actor") else n = tonumber(nvis.v1) if n then obj = level_object_by_sid(n) if obj and obj:alive() then nvis.v1 = obj self.on_target_nvis = nvis printf("car : target %d", n) end end end end self.path_fire = nil self.path_fire_info = nil self.fire_pt_count = 0 self.def_fire_time = self.st.fire_time if self.st.fire_rep then if self.st.fire_rep == "inf" then self.def_fire_rep = -1 else local c = tonumber(self.st.fire_rep) self.def_fire_rep = c > 0 and c or 0 end else self.def_fire_rep = 0 end self.fire_rep = self.def_fire_rep printf("car : def_rep = %d (%s)", self.fire_rep, utils_data.to_str(self.st.fire_rep)) self.fire_range_sqr = self.st.fire_range * self.st.fire_range -- path_fire if self.state_firetarget == state_firetarget_points and self.st.path_fire then printf("car : firetarget = points") self.path_fire = patrol(self.st.path_fire) if not self.path_fire then printf("object '%s': unable to find path_fire '%s' on the map", self.object:name(), self.st.path_fire) end if not self.path_fire_info then self.path_fire_info = utils_obj.path_parse_waypoints(self.st.path_fire) if not self.path_fire_info then printf("object '%s': path_fire ('%s'): unable to obtain path_fire_info from path", self.object:name(), self.st.path_fire) end end -- точки прострела для первой точки движения self:change_fire_pts() if self.st.auto_fire then self.car:Action(CCar.eWpnAutoFire, 1) else self.car:Action(CCar.eWpnAutoFire, 0) end self:fire_arrival_callback(self.target_fire_pt_idx) --self:rot_to_firepoint(self.target_fire_pt) --self:set_shooting(self.state_shooting) elseif self.state_firetarget == state_firetarget_enemy then printf("car : firetarget = enemy") self.state_shooting = state_none self.state_cannon = state_cannon_follow --self.target_fire_pt = db.actor:position() --self:rot_to_firepoint(self.target_fire_pt) else printf("car : firetarget = none") self.state_firetarget = state_none self.state_cannon = state_none self.state_shooting = state_none end --self:set_shooting(self.state_shooting) end --if self.st.path_walk then -- if self:at_target_walkpoint() and self.state_firetarget == state_none then -- self:walk_arrival_callback(self.target_walk_pt) -- end -- -- self:go_to_walkpoint(self.target_walk_pt) --end printf("car : start target_pt2 = %d", self.target_walk_pt) end self.object:set_fastcall(self.fastcall, self) printf("car : END INITIALIZING ========================================================\n") end function action_car:save() printf("car : saving") if self.usable == nil then save_var(self.object, "speed", self.speed) save_var(self.object, "loop", self.loop) save_var(self.object, "min_delta_per_sec", self.min_delta_per_sec) save_var(self.object, "min_car_explode_time", self.min_car_explode_time) save_var(self.object, "state_moving", self.state_moving) save_var(self.object, "target_walk_pt", self.target_walk_pt) -- or -2) printf("car : target_walk_pt(%d)", self.target_walk_pt) end end --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- --++++++++++++++++++++-- MOVE SECTION --++++++++++++++++++++-- --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- function action_car:get_nearest_walkpoint() return utils_obj.get_nearest_waypoint(self.object, self.st.path_walk, self.path_walk, self.path_walk_count) end function action_car:get_next_walkpoint() if self.target_walk_pt >= 0 and self.target_walk_pt < self.path_walk_count - 1 then printf("car : action_car:get_next_walkpoint(%d)", self.target_walk_pt + 1) return self.target_walk_pt + 1 elseif self.target_walk_pt == -1 or self.loop then printf("car : action_car:get_next_walkpoint(0)") return 0 end printf("car : action_car:get_next_walkpoint(-2)") return -2 end function action_car:at_target_walkpoint() printf("car : action_car:at_target_walkpoint(%s)", utils_data.to_str(self.target_walk_pt)) if self.target_walk_pt == -2 then return true end local curVel = self.car:CurrentVel():magnitude() local dist = self.object:position():distance_to(self.path_walk:point(self.target_walk_pt)) --local arrived = 2 * dist < def_update_time * (curVel.x + self.speed) --def_update_time > 2 * dist / (curVel.x + self.speed) --local arrived = dist < curVel.x * def_arriving_factor --8 -- def_arriving_koef * self.fc_upd_avg / 1000 > 2 * dist / (curVel.x + self.speed) local arrived = false --2000 * dist < def_arriving_koef * self.fc_upd_avg * (curVel + self.speed) if self.fc_upd_avg < 100 then arrived = 2000 * dist < def_arriving_koef * self.fc_upd_avg * (curVel + self.speed) printf("car : check formula (%f < %f)", 2000 * dist, def_arriving_koef * self.fc_upd_avg * (curVel + self.speed)) if arrived then printf("car : at target pt by formula.") end end if not arrived then local diff_angle = angle_xz(self.object, self.path_walk:point(self.target_walk_pt)) printf("car : dist(%f), angle(%f).", dist, diff_angle) arrived = dist < 2 or diff_angle >= math.pi if arrived then --printf("car : at target pt by dist(%f) and angle(%f).", dist, diff_angle) printf("car : at target pt by dist and angle.") end end local diff = dist - self.target_dist printf("car : dist = %f, target = %f, diff = %f, curVel = %f, speed = %f", dist, self.target_dist, diff, curVel, self.speed) if self.target_dist ~= -1 and not arrived then if diff > 0 then if diff > def_arriving_dist then arrived = true printf("car : at target pt by diff(%f).", diff) end else self.target_dist = dist end else self.target_dist = dist end --[[ --printf("car : action_car:at_target_walkpoint(self.target_walk_pt=%d) = %d (arrived = %s)", self.target_walk_pt, dist, utils_data.to_str(arrived)) --if arrived then printf("car : arrived = %f > %f, dist(%f), diff(%f), curSpd(%f), spd(%f)", --def_arriving_koef * self.fc_upd_avg / 1000, --2 * dist / (curVel.x + self.speed), def_arriving_koef * self.fc_upd_avg * (curVel + self.speed), 2000 * dist, dist, diff, curVel, self.speed) --printf("car : arrived = %f < (%f,%f,%f) * %f, spd(%f)", dist, curVel.x, curVel.y, curVel.z, def_arriving_factor, self.speed) --end --]] if arrived then self.target_dist = -1 end return arrived end function action_car:go_to_walkpoint(pt) --action(self.object, object("left_light", object.activate), cond(cond.time_end, 1000)) --action(self.object, object("right_light", object.activate), cond(cond.time_end, 1000)) printf("car : action_car:go_to_walkpoint(%s)", utils_data.to_str(pt)) if self.state_delaying then if time_global() - self.delay_time_start >= delay_after_ignition then printf("car : stop delaying after ignition") self.state_delaying = false else printf("car : delaying after ignition") self.state_moving = state_moving_stop return end end if pt == nil or pt < 0 then if self.state_cannon ~= state_cannon_stop then self:stop_car() self.state_moving = state_moving_end end printf("car : pt < 0 or pt == nil") return end local diff_angle = angle_xz(self.object, self.path_walk:point(pt)) local accel = move.none local curVel = self.car:CurrentVel():magnitude() printf("car : curVel = %f, speed = %f", curVel, self.speed) if curVel < self.speed then accel = move.fwd printf("car : forward") elseif curVel > self.speed * 1.15 or diff_angle >= math.pi then accel = move.back printf("car : backward") end --printf("car : diff_angle = %f, katet = %f", diff_angle, get_katet(self.object, self.path_walk:point(pt))) --if utils_data.no_need_to_rotate_xz(self.object, self.path_walk:point(pt)) then if diff_angle < def_not_rotating_angle then if self.state_moving ~= state_moving_fwd and accel ~= move.none then printf("car : no_need_to_rotate. speed = %f, time = %f", self.speed, self.fc_upd_avg) xr_logic.mob_capture(self.object, true) action(self.object, move(accel, self.speed), --move.on + --object("right_light", self.headlights), --object("left_light", self.headlights), cond(cond.time_end, time_infinite)) --self.fc_upd_avg)) self.state_moving = state_moving_fwd end else -- Поворачиваться нужно, но в какую сторону? local rotate_left = utils_data.angle_left_xz(self.object:direction(), vector():set(self.path_walk:point(pt) ):sub(self.object:position())) printf("car : must rotate: left? %s. speed = %f, time = %f", utils_data.to_str(rotate_left), self.speed, self.fc_upd_avg) if rotate_left then if self.state_moving ~= state_moving_rot_left then xr_logic.mob_capture(self.object, true) action(self.object, move(accel + move.left, self.speed), --move.on + --object("right_light", self.headlights), --object("left_light", self.headlights), cond(cond.time_end, time_infinite)) --self.fc_upd_avg)) self.state_moving = state_moving_rot_left end else if self.state_moving ~= state_moving_rot_right then xr_logic.mob_capture(self.object, true) action(self.object, move(accel + move.right, self.speed), --move.on + --object("right_light", self.headlights), --object("left_light", self.headlights), cond(cond.time_end, time_infinite)) --self.fc_upd_avg)) self.state_moving = state_moving_rot_right end end end end function action_car:start_car() printf("car : car started.") xr_logic.mob_capture(self.object, true) action(self.object, move(move.on + move.fwd, 100), cond(cond.time_end, 5000)) --self.speed = 0 self.state_delaying = true self.delay_time_start = time_global() end function action_car:stop_car() printf("car : car stopping.") self.state_delaying = false self.speed = 0 xr_logic.mob_capture(self.object, true) action(self.object, move(move.off + move.handbrake, 0), cond(cond.time_end, 3000)) --cond(cond.move_end)) --self.target_walk_pt = -1 end function action_car:walk_arrival_callback(index) printf("car : action_car:walk_arrival_callback(%d)", index) if index == -2 then return end local suggested_explode = self.path_walk_info[index]["explode"] if suggested_explode == "true" then -- printf("action_car:walk_arrival_callback(): EXPLODE") --self:car_explode() self.object.health = 0 self.car:Action(CCar.eWpnActivate, 0) end local suggested_spd = self.path_walk_info[index]["spd"] if suggested_spd then self.speed = tonumber(suggested_spd) else self.speed = def_moving_speed end --printf("car : new spd = %f", self.speed) local suggested_loop = self.path_walk_info[index]["loop"] if suggested_loop == "true" then self.loop = true else self.loop = false end local suggested_dps = self.path_walk_info[index]["dps"] if suggested_dps then self.min_delta_per_sec = tonumber(suggested_dps) else self.min_delta_per_sec = def_min_delta_per_sec end local suggested_exptm = self.path_walk_info[index]["exptm"] if suggested_exptm then self.min_car_explode_time = tonumber(suggested_exptm) else self.min_car_explode_time = def_min_car_explode_time end local sig = self.path_walk_info[index]["sig"] --printf("car : try") if sig then --self.st.signals[sig] = true self:set_signal(sig) --printf("car : %s", sig) end if self.hasWeapon and self.state_firetarget == state_firetarget_points then local next_idx = self:get_next_walkpoint() --local next_idx = self.target_walk_pt if next_idx >= 0 then local fw = self.path_walk_info[next_idx]["fw"] if fw then self.fire_wait = fw == "true" else self.fire_wait = false end local fr = self.path_walk_info[next_idx]["fr"] if fr then if fr == "inf" then self.fire_rep = -1 else local c = tonumber(fr) self.fire_rep = c > 0 and c or 0 end else self.fire_rep = self.def_fire_rep end else self.fire_wait = false self.fire_rep = self.def_fire_rep end printf("car : fire_wait = %s", self.fire_wait and "true" or "false") printf("car : fire_rep = %d", self.fire_rep) end -- Выбрать следующую точку езды: self.target_walk_pt = self:get_next_walkpoint() printf("car : target_walk_pt = %s.", utils_data.to_str(self.target_walk_pt)) --if not self.target_walk_pt then -- printf("car : stop moving.") -- self:stop_car() -- Остановить машину --if self.state_cannon == state_cannon_stop then -- xr_logic.mob_capture(self.object, true) --end --action(self.object, move(move.off, 0), cond(cond.time_end, 1000)) --self.state_moving = state_moving_end -- return --end if self.target_walk_pt >= 0 then -- Если эта точка рядом и уже приехали - не ждать апдейта, чтобы вызвать callback if self:at_target_walkpoint() then self:walk_arrival_callback(self.target_walk_pt) else --self.target_dist = -1 self:go_to_walkpoint(self.target_walk_pt) end else printf("car : stop moving.") self.target_walk_pt = -2 if self.state_moving ~= state_moving_stop and self.state_moving ~= state_moving_end then self:stop_car() self.state_moving = state_moving_end end end end --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- --++++++++++++++++++++-- FIRE SECTION --++++++++++++++++++++-- --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- function action_car:change_fire_pts() printf("car : action_car:change_fire_pts()") self.state_cannon = state_none self.state_shooting = state_none self.fire_pt_arr = {} self.fire_pt_count = 0 self.target_fire_pt_idx = 0 self.target_fire_pt = nil self.fire_rot_dir = 1 self.fire_start_time = 0 if not self.path_fire then return end local fire_idx if self.target_walk_pt >= 0 then -- Значение флагов огневых точек, котые будем искать: local fire_flags = self.path_walk_info[self.target_walk_pt].flags if fire_flags:get() == 0 then return end local this_val for fire_idx = 0, self.path_fire:count() - 1 do this_val = self.path_fire_info[fire_idx].flags if this_val:equal(fire_flags) then table.insert(self.fire_pt_arr, fire_idx) self.fire_pt_count = self.fire_pt_count + 1 end end else for fire_idx = 0, self.path_fire:count() - 1 do table.insert(self.fire_pt_arr, fire_idx) end self.fire_pt_count = self.path_fire:count() end printf("car : points") print_table(self.fire_pt_arr) if self.fire_pt_count == 0 then self.state_cannon = state_cannon_stop return end --if self.fire_pt_count > 0 then -- self.target_fire_pt_idx = 0 -- self.target_fire_pt = self.path_fire:point(self.fire_pt_arr[0]) --end end function action_car:get_next_firepoint() if self.fire_pt_count < 1 then return nil end printf("car : action_car:get_next_firepoint(%d,%d,%d)", self.target_fire_pt_idx, self.fire_pt_count, self.fire_rep) local pt_idx if self.target_fire_pt_idx > 0 then -- not first time if self.fire_pt_count > 1 then -- we have at least 2 point to switch pt_idx = self.target_fire_pt_idx + self.fire_rot_dir if (pt_idx < 1 or pt_idx > self.fire_pt_count) then self.fire_rot_dir = -self.fire_rot_dir -- change rotate direction [-1,1] printf("car : changing direction") if self.fire_rep == -1 then pt_idx = self.target_fire_pt_idx + self.fire_rot_dir elseif self.fire_rep > 0 then pt_idx = self.target_fire_pt_idx + self.fire_rot_dir self.fire_rep = self.fire_rep - 1 else pt_idx = 0 end end elseif self.fire_rep == -1 then -- left same point in the infinity loop pt_idx = 1 elseif self.fire_rep > 0 then -- left same point if loop defined pt_idx = 1 self.fire_rep = self.fire_rep - 1 end else pt_idx = 1 end --self.target_fire_pt_idx = pt_idx printf("car : pt_idx = %d", pt_idx) if pt_idx > 0 then return pt_idx, self.path_fire:point(self.fire_pt_arr[pt_idx]) end self.state_firetarget = state_none return pt_idx, nil end function action_car:rot_to_firepoint(pt) --[[ if pt then self.car:SetParam(CCar.eWpnDesiredPos, pt) end printf("car : action_car:rot_to_firepoint(%d)", shooting) self.car:Action(CCar.eWpnFire, shooting) --]] if self.target_fire_pt then -- self.state_cannon = state_cannon_rotate self.car:SetParam(CCar.eWpnDesiredPos, self.target_fire_pt) printf("car : action_car:rot_to_firepoint(%d)", self.target_fire_pt_idx) --else -- self.state_cannon = state_cannon_stop end end function action_car:set_shooting(shooting) self.car:Action(CCar.eWpnFire, shooting) printf("car : action_car:set_shooting(%d)", shooting) end function action_car:fire_arrival_callback(cur_index) if self.fire_pt_count < 1 or self.state_cannon == state_cannon_stop then return end printf("car : action_car:fire_arrival_callback(%s)", utils_data.to_str(cur_index)) if self.state_cannon == state_cannon_delay then printf("car : 1.delay mode off.") -- Выбрать следующую точку езды: self.target_fire_pt_idx, self.target_fire_pt = self:get_next_firepoint() if self.target_fire_pt then printf("car : 1.rotate mode on.") self.state_cannon = state_cannon_rotate if self.fire_rot_dir == 1 and self.fire_forward or self.fire_rot_dir == -1 and self.fire_backward then printf("car : 1.shooting on.") self.state_shooting = state_shooting_on else printf("car : 1.shooting off.") self.state_shooting = state_none end self:rot_to_firepoint(self.target_fire_pt) self:set_shooting(self.state_shooting) else printf("car : 1.cannon stopped.") self.state_cannon = state_cannon_stop if self.state_shooting ~= state_none then self.state_shooting = state_none self:set_shooting(self.state_shooting) self:set_signal("fire_end") end end return end --printf("car : action_car: fire_arrival_callback(%s)", utils_data.to_str(cur_index)) if cur_index > 0 then local idx = self.fire_pt_arr[cur_index] local ff = self.path_fire_info[idx]["ff"] if ff then self.fire_forward = ff == "true" else self.fire_forward = false end local fb = self.path_fire_info[idx]["fb"] if fb then self.fire_backward = fb == "true" else self.fire_backward = false end local f = self.path_fire_info[idx]["f"] if f then self.state_shooting = f == "true" and state_shooting_on or state_none else self.state_shooting = self.st.auto_fire and state_shooting_on or state_none end local ft = self.path_fire_info[idx]["ft"] if ft then self.fire_time = tonumber(ft) else self.fire_time = self.def_fire_time end else self.fire_forward = false self.fire_backward = false self.state_shooting = if_then_else(self.st.auto_fire, state_shooting_on, state_none) self.fire_time = def_min_fire_time end printf("car : state_shooting = %s",self.state_shooting ~= 0) --printf("car : auto_fire = %s", if_then_else(self.st.auto_fire, "true", "false")) printf("car : fire_time = %f", self.fire_time) if self.fire_time > 0 then printf("car : 2.delay mode on.") self.state_cannon = state_cannon_delay self.fire_start_time = time_global() --printf("car : state_shooting = %s", if_then_else(self.state_shooting ~= 0, "true", "false")) --self:rot_to_firepoint(nil) self:set_shooting(self.state_shooting) else -- Выбрать следующую точку стрельбы: self.target_fire_pt_idx, self.target_fire_pt = self:get_next_firepoint() -- Если мы уже целимся в эту точку - не ждать апдейта, чтобы вызвать callback if self.target_fire_pt then printf("car : 2.rotate mode on.") self.state_cannon = state_cannon_rotate if self.fire_rot_dir == 1 and self.fire_forward or self.fire_rot_dir == -1 and self.fire_backward then self.state_shooting = state_shooting_on printf("car : 2.shooting on.") else self.state_shooting = state_none printf("car : 2.shooting off.") end self:rot_to_firepoint(self.target_fire_pt) self:set_shooting(self.state_shooting) else printf("car : 2.cannon stopped.") self.state_cannon = state_cannon_stop if self.state_shooting ~= state_none then self.state_shooting = state_none self:set_shooting(self.state_shooting) self:set_signal("fire_end") end end end end --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- --+++++++++++++++++++-- COMMON SECTION --+++++++++++++++++++-- --++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- function action_car:set_signal(sig) local stor = db.storage[self.object:id()] stor[stor.active_scheme].signals[sig] = true printf("car : %s", sig) end function angle_xz(npc, target_pos) local dir1 = npc:direction() dir1.y = 0 local dir2 = vector():set(target_pos):sub(npc:position()) dir2.y = 0 local dp = dir1:dotproduct(dir2) if dp < 0 then return math.pi - yaw(dir1, dir2) end return yaw(dir1, dir2) end function get_katet(npc, target_pos) local p = vector():set(target_pos):sub(npc:position()) local q = npc:direction() local k = p.x * q.x + p.y * q.y if k ~= 0 then return target_pos:distance_to(npc:position()) * (p.x * q.y - p.y * q.x) / k end return -1 end function action_car:fastcall() if db.storage[self.object:id()].active_scheme ~= "ph_car" then -- Если активная схема - не машина, снять быстрый апдейт return true end if self.usable ~= nil then local cu = xr_logic.pick_section_from_condlist(db.actor, self.object, self.usable.condlist) local u = (cu == "true" or cu == "") if u then if self.show_tips and self.tip_use then self.object:set_tip_text(self.st.tip_use) end elseif self.show_tips and self.tip_locked then self.object:set_tip_text(self.st.tip_locked) end self.object:set_nonscript_usable(u) return false end return self:fast_update() end function action_car:update(delta) if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then return end if self.destroyed then xr_logic.switch_to_section(self.object, self.st.ini, "nil") return end if self.st.invulnerable then self.object.health = self.def_health end end function action_car:destroy_car() printf("car : START ===============================") self.state_moving = state_moving_end self:stop_car() self.state_cannon = state_none self.state_firetarget = state_none self.state_shooting = state_none self.car:Action(CCar.eWpnAutoFire, 0) self:set_shooting(self.state_shooting) -- turn off lights xr_logic.mob_capture(self.object, true) action(self.object, object("left_light", object.deactivate), cond(cond.time_end, time_infinite)) xr_logic.mob_capture(self.object, true) action(self.object, object("right_light", object.deactivate), cond(cond.time_end, time_infinite)) xr_logic.mob_release(self.object) if self.st.on_death_info ~= nil then db.actor:give_info_portion(self.st.on_death_info) printf("car : on_death_info [%s]", self.st.on_death_info) end self.destroyed = true printf("car : END =================================") end -- Вернуть true, если апдейты больше не нужны function action_car:fast_update() printf("car : START FAST UPDATE ======================================================") printf("car : check moving1 - [%d][%d]", self.state_moving, self.target_walk_pt) --printf("car : action_car:update(): state=%d", self.state_moving) --if not self.object:action() then -- printf("_pc: CAR EXPLODE") -- self.object:explode(0) -- self.object:Explode() --end if self.car:GetfHealth() <= 0 then printf("car : killed.") --self:car_explode() self:destroy_car() return true end local cur_time = time_global() if self.fc_upd_num < def_max_fc_upd_num then local last_upd = self.fc_last_upd_tm if last_upd ~= -1 then local n = self.fc_upd_num if n < 3000 then self.fc_upd_avg = (self.fc_upd_avg * n + (cur_time - last_upd))/(n + 1) self.fc_upd_num = n + 1 else self.fc_upd_num = 1 end end self.fc_last_upd_tm = cur_time printf("car : average update = %f, time(%f)", self.fc_upd_avg, cur_time) end if self.state_moving == state_moving_end and self.state_cannon == state_cannon_stop and self.state_firetarget == state_none then if xr_logic.mob_captured(self.object) and not self.object:action() then --printf("car : stop fast updating. moving(%s) cannon(%s)", utils_data.to_str(self.state_moving), utils_data.to_str(self.state_cannon)) --xr_logic.mob_release(self.object) --if self.st.on_death_info ~= nil then -- printf("car : stopped") -- db.actor:give_info_portion(self.st.on_death_info) --end printf("car : stopped") self:destroy_car() return true -- апдейты больше не нужны end return false end if self.state_moving ~= state_moving_end and self.state_moving ~= state_moving_stop and cur_time >= self.last_pos_time + self.min_car_explode_time then printf("car : moving(%s)", utils_data.to_str(self.state_moving)) if not self.last_pos then self.last_pos = self.object:position() self.last_pos_time = cur_time else local cur_pos = self.object:position() local diff = self.last_pos:distance_to(cur_pos) if diff < self.min_delta_per_sec then printf("car : got stuck (%f < %f) - stop.", diff, self.min_delta_per_sec) self:stop_car() --self:car_explode() self.state_moving = state_moving_end --return false else self.last_pos = cur_pos end self.last_pos_time = cur_time end end printf("car : check moving2 - [%d][%d]", self.state_moving, self.target_walk_pt) if self.state_moving ~= state_moving_end and self.state_moving ~= state_none then printf("car : try") if self.target_walk_pt >= 0 then if self:at_target_walkpoint() then printf("car : at target point") -- если нет флага ожидания стрельбы или мы уже отстрелялись или пошли в цикле обратно if not self.fire_wait or self.state_cannon == state_cannon_stop then --self.fire_rot_dir == -1 then --printf("car : car go to the new point.") -- Прибыли, выбрать новую точку self:walk_arrival_callback(self.target_walk_pt) printf("car : new target = %s", utils_data.to_str(self.target_walk_pt)) -- меняем точки отстрела self:change_fire_pts() else printf("car : 1.car stopped.") if self.state_moving ~= state_moving_stop then self:stop_car() end self.state_moving = state_moving_stop end else printf("car : car keep going.") -- Продолжать ехать, или поворачиваться self:go_to_walkpoint(self.target_walk_pt) end else printf("car : 2.car stopped.") if self.state_cannon ~= state_cannon_stop then self:stop_car() self.state_moving = state_moving_stop end end end --printf("car : TEST") if self.hasWeapon then --printf("car : target(%d)", self.state_firetarget) if self.on_target_vis and self.on_target_vis.v1:alive() and self.car:IsObjectVisible(self.on_target_vis.v1) then --printf("car : try") local new_section = xr_logic.pick_section_from_condlist(db.actor, self.object, self.on_target_vis.condlist) if new_section then printf("car : switch to section [%s]", new_section) xr_logic.switch_to_section(self.object, self.st.ini, new_section) end end if self.on_target_nvis and self.on_target_nvis.v1:alive() and not self.car:IsObjectVisible(self.on_target_nvis.v1) then --printf("car : try") local new_section = xr_logic.pick_section_from_condlist(db.actor, self.object, self.on_target_nvis.condlist) if new_section then printf("car : switch to section [%s]", new_section) xr_logic.switch_to_section(self.object, self.st.ini, new_section) end end if self.state_firetarget == state_firetarget_points then if self.fire_pt_count > 0 and self.state_cannon ~= state_cannon_stop then --printf("car : shooting points") if self.state_cannon == state_cannon_delay then if self.fire_start_time + self.fire_time < cur_time then --printf("car : stop delaying(%.2f + %.2f < %.2f)", self.fire_start_time, self.fire_time, time_global()) printf("car : 1.new fire point.") self:fire_arrival_callback(self.target_fire_pt_idx) else printf("car : delaying(%.2f + %.2f < %.2f)", self.fire_start_time, self.fire_time, cur_time) end else if self.car:CanHit() or self.target_fire_pt_idx == 0 then printf("car : 2.new fire point.") -- Прибыли, выбрать новую точку self:fire_arrival_callback(self.target_fire_pt_idx) end end end elseif self.state_firetarget == state_firetarget_enemy then if self.target_obj:alive() and self.object:position():distance_to_sqr(self.target_obj:position()) <= self.fire_range_sqr and self.car:IsObjectVisible(self.target_obj) then printf("car : enemy(%s) is visible.", self.target_obj:name()) self.target_fire_pt = self.target_obj:position() if self.target_obj:id() ~= AC_ID then if self.target_obj:target_body_state() == move.crouch then self.target_fire_pt.y = self.target_fire_pt.y + 0.6 -- FAKE elseif not xr_wounded.is_heavy_wounded_by_id(self.target_obj:id()) then self.target_fire_pt.y = self.target_fire_pt.y + 1.4 -- FAKE else self.target_fire_pt.y = self.target_fire_pt.y + 0.15 -- FAKE end end self:rot_to_firepoint(self.target_fire_pt) if self.car:CanHit() then if self.state_shooting == state_none then printf("car : shooting enemy (first).") self.state_shooting = state_shooting_on self:set_shooting(self.state_shooting) --self:set_signal("fire_start") end printf("car : shooting enemy.") else if self.state_shooting ~= state_none then printf("car : targeting enemy (first).") self.state_shooting = state_none self:set_shooting(self.state_shooting) --self:set_signal("fire_end") end printf("car : targeting enemy.") end else if self.state_shooting ~= state_none then printf("car : enemy isn't visible (first).") self.state_shooting = state_none self:set_shooting(self.state_shooting) --self:set_signal("fire_end") end printf("car : enemy isn't visible.") if self.fire_track_target then self.target_fire_pt = self.target_obj:position() self.target_fire_pt.y = self.target_fire_pt.y + 1.4 -- FAKE self:rot_to_firepoint(self.target_fire_pt) printf("car : target tracking.") end end end end printf("car : check moving3 - [%d][%d]", self.state_moving, self.target_walk_pt) printf("car : END FAST UPDATE ========================================================\n") return false end function action_car:car_explode() self.object:explode(time_global()) end function action_car:net_destroy() --self:destroy_car() if self.object and xr_logic.mob_captured(self.object) then xr_logic.mob_release(self.object) if self.st.on_death_info ~= nil then db.actor:give_info_portion(self.st.on_death_info) printf("car : give info") end end end --------------------------------------------------------------------------------------------------------------------- function add_to_binder(npc, ini, scheme, section, storage) -- printf("DEBUG: add_to_binder: scheme='%s', section='%s'", scheme, section) local new_action = action_car(npc, storage) -- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы: xr_logic.subscribe_action_for_events(npc, storage, new_action) end function set_scheme(npc, ini, scheme, section, gulag_name) local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section) st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc) st.usable = xr_logic.cfg_get_condlist(ini, section, "usable", npc) if st.usable == nil then st.path_walk = utils_data.prefix_r_string(ini,section,"path_walk",gulag_name) st.path_fire = utils_data.prefix_r_string(ini,section,"path_fire",gulag_name) st.auto_fire = ini:r_bool_ex(section,"auto_fire",false) st.fire_time = ini:r_float_ex(section,"fire_time") or def_min_fire_time st.fire_rep = ini:r_string_ex(section,"fire_repeat") st.fire_range = ini:r_float_ex(section,"fire_range") or def_fire_range st.fire_target = ini:r_string_ex(section,"target") or "points" st.fire_track_target= ini:r_bool_ex(section,"track_target",false) st.on_target_vis = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_vis", npc) st.on_target_nvis = xr_logic.cfg_get_string_and_condlist(ini, section, "on_target_nvis", npc) st.path_walk_info = nil -- Будут инициализированы в reset(), сейчас пути могут быть еще не загружены. st.path_fire_info = nil else st.show_tips = ini:r_bool_ex(section,"show_tips",true) st.tip_use = ini:r_string_ex(section,"tip_use") or "tip_car_use" st.tip_locked = ini:r_string_ex(section,"tip_locked") or "tip_car_locked" end st.invulnerable = ini:r_bool_ex(section,"invulnerable",false) st.headlights = ini:r_string_ex(section,"headlights") or "off" st.on_death_info = ini:r_string_ex(section,"on_death_info") end